package org.nocket.gen.page.element.synchronizer;
import gengui.annotations.Assisted;
import gengui.annotations.Eager;
import gengui.annotations.Forced;
import gengui.annotations.Prompt;
import gengui.annotations.Validate;
import gengui.domain.AbstractDomainReference;
import gengui.domain.DomainObjectDecoration;
import gengui.domain.DomainObjectReference;
import gengui.guiadapter.DateSynchronizer;
import gengui.guiadapter.GUIItemFactory;
import gengui.guibuilder.FormBuilder;
import gengui.swing.AbstractRootFrame;
import gengui.util.AnnotationHelper;
import gengui.util.DefaultSevereExceptionHandler;
import gengui.util.ReflectionUtil;
import gengui.util.SevereGUIException;
import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.validation.constraints.NotNull;
import org.apache.commons.lang.StringUtils;
import org.apache.wicket.Component;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.extensions.markup.html.tabs.TabbedPanel;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.RadioChoice;
import org.apache.wicket.model.IModel;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.util.visit.ClassVisitFilter;
import org.apache.wicket.util.visit.IVisit;
import org.apache.wicket.util.visit.IVisitor;
import org.apache.wicket.util.visit.Visits;
import org.nocket.component.table.GenericDataTablePanel;
import org.nocket.gen.domain.element.DomainElementI;
import org.nocket.gen.i18n.I18NLabelModelFactory;
import org.nocket.gen.page.DMDWebGenPageContext;
import org.nocket.gen.page.element.FeedbackElement;
import org.nocket.gen.page.element.PageElementI;
import org.nocket.gen.page.element.synchronizer.error.MethodExceptionHandlerI;
import org.nocket.gen.page.element.synchronizer.error.WicketGenguiSevereExceptionHandler;
import org.nocket.gen.page.visitor.bind.builder.components.GeneratedButton;
import org.nocket.gen.page.visitor.bind.builder.components.GeneratedNumberTextField;
public class SynchronizerHelper implements Serializable {
private static final long serialVersionUID = 1L;
private DMDWebGenPageContext context;
private String wicketId;
private String propertyName;
private boolean enableThoughUnmodifiable;
private transient DomainObjectReference ref;
private transient Method buttonMethod;
private transient Method choicerMethod;
private transient Method getterMethod;
private transient Method setterMethod;
private transient Method disablerMethod;
private transient Method hiderMethod;
private transient Method validatorMethod;
private transient Method removerButtonMethod;
private transient Boolean isEager;
private transient Boolean isStaticallyDisabled;
private transient String help;
private transient String prompt;
private transient Object forcedMethodTargetObject;
private transient boolean transientFieldsInitializedFromPrototype;
private static final Map<String, SynchronizerHelper> synchronizerPrototypes = new HashMap<String, SynchronizerHelper>();
private static final Method NULL_METHOD;
static {
WicketGenguiSevereExceptionHandler.init();
Method m = null;
try {
m = SynchronizerHelper.class.getDeclaredMethod("NULL_METHOD");
} catch (NoSuchMethodException nsmx) {
System.err.println("Can't initialize NULL_METHOD");
nsmx.printStackTrace();
}
NULL_METHOD = m;
}
public SynchronizerHelper(PageElementI<?> element) {
this(element.getContext(), element.getDomainElement().getWicketId(), element.getDomainElement().getPropertyName(), element
.enableThoughUnmodifiable());
}
public SynchronizerHelper(DMDWebGenPageContext context, DomainElementI<DomainObjectReference> domainElement) {
this(context, domainElement.getWicketId(), domainElement.getPropertyName(), false);
}
public SynchronizerHelper(DMDWebGenPageContext context, String wicketId, String propertyName, boolean enableThoughUnmodifiable) {
this.context = context;
this.enableThoughUnmodifiable = enableThoughUnmodifiable;
context.getPage().add(new Behavior() {
private static final long serialVersionUID = 1L;
@Override
public void afterRender(Component component) {
super.afterRender(component);
// domainreference needs to be reset on ajax updates
ref = null;
}
});
this.wicketId = wicketId;
this.propertyName = propertyName;
saveAsPrototype();
}
protected void saveAsPrototype() {
// Some SynchronizerHelpers are not instanciated for being part of a model but really just
// as a temporary helper (e.g. see method GeneratedGenericDataTableFactory#addColumnContentConverter).
// This may have the effect that there is not (yet) a valid object
// reference available which in turn would inhibit the crucial meta data retrieval required for a real good
// prototype. So we just don't consider these instances for prototypes.
if (getRef() == null)
return;
synchronized (SynchronizerHelper.class) {
String syncName = synchronizerLookupName();
SynchronizerHelper syncPrototype = synchronizerPrototypes.get(syncName);
if (syncPrototype == null) {
// Following get... function calls ensure a maximum retrieval of meta data for
// the prototype so that derived instances have a minimum retrieval work.
// SynchronizerHelpers are serialized and deserialized very often so that
// meta data retrieval from reflection calls would otherwise make up a significant
// percentage of time (according to profiling from 23.03.2013)
getGetterMethod();
getSetterMethod();
getChoicerMethod();
getDisablerMethod();
getHiderMethod();
getValidatorMethod();
getButtonMethod();
getRemoverButtonMethod();
isEager();
transientFieldsInitializedFromPrototype = true;
synchronizerPrototypes.put(syncName, this);
}
}
}
protected String synchronizerLookupName() {
String domainClassName = context.getPage().getDefaultModelObject().getClass().getName();
return domainClassName + "#" + wicketId;
}
protected Class<?> getClassForResourceLookup() {
return context.getDomainRegistry().getElement(wicketId).getAccessor().getClassRef().getDomainClass();
}
public String getWicketId() {
return wicketId;
}
public String getPropertyName() {
return propertyName;
}
public DMDWebGenPageContext getContext() {
return context;
}
public Object invokeButtonMethod(MethodExceptionHandlerI methodExceptionHandler) {
return invokeMethod(methodExceptionHandler, "", getButtonMethod(), new Object[] {});
}
public Object invokeChoicerMethod() {
return invokeMethod(null, ReflectionUtil.CHOICE_PREFIX, getChoicerMethod());
}
public Object invokeGetterMethod() {
return invokeMethod(null, ReflectionUtil.GETTER_PREFIX, getGetterMethod());
}
public Object invokeSetterMethod(Object... params) {
return invokeMethod(null, ReflectionUtil.SETTER_PREFIX, getSetterMethod(), params);
}
public Object invokeDisablerMethod() {
return invokeMethod(null, ReflectionUtil.DISABLER_PREFIX, getDisablerMethod());
}
public Object invokeHiderMethod() {
return invokeMethod(null, ReflectionUtil.HIDE_PREFIX, getHiderMethod());
}
public Object invokeValidatorMethod(Object newValue) {
return invokeMethod(null, ReflectionUtil.VALIDATOR_PREFIX, getValidatorMethod(), newValue);
}
public void invokeRemoverButtonMethod(MethodExceptionHandlerI methodExceptionHandler, Object... removedValues) {
Object[] params = removedValues;
if (ReflectionUtil.hasMultivaluedParameter(getRemoverButtonMethod())) {
Class<?>[] paramTypes = getRemoverButtonMethod().getParameterTypes();
Class<?> multiselectionType = paramTypes[0].getComponentType();
if (multiselectionType != null) {
Object[] typedArray = (Object[]) Array.newInstance(multiselectionType, removedValues.length);
for (int i = 0; i < removedValues.length; i++) {
typedArray[i] = removedValues[i];
}
params = new Object[] { typedArray };
}
}
invokeMethod(methodExceptionHandler, ReflectionUtil.REMOVER_PREFIX, getRemoverButtonMethod(), params);
}
private Object invokeMethod(MethodExceptionHandlerI exHandler, String methodType, Method method, Object... params) {
Object domainObject = getRef().getDomainObject();
if (domainObject == null)
return null;
Object targetObject = getMethodTargetObject(methodType);
try {
return ReflectionUtil.invokeMethodAllowExceptions(method, targetObject, params);
} catch (InvocationTargetException e) {
Object domainObjectForException = domainObject instanceof Collection || domainObject.getClass().isArray() ? targetObject
: domainObject;
if (exHandler == null || !handleException(exHandler, domainObjectForException, e.getTargetException())) {
throw DefaultSevereExceptionHandler.handler().process(e.getTargetException());
}
return null;
}
}
private synchronized boolean handleException(MethodExceptionHandlerI exHandler, Object domainObject, Throwable exception) {
boolean swallowed = false;
boolean swallowable = true;
Throwable child = exception;
while (child != null) {
if (ReflectionUtil.isUnswallowable(child) || child instanceof RuntimeException || child instanceof Error) {
swallowable = false;
break;
}
child = child.getCause();
}
if (swallowable) {
if (exception.getMessage() != null) {
String title = (domainObject instanceof Class) ? FormBuilder.buildPrompt((Class<?>) domainObject) : // Exception
// in
// constructor
ReflectionUtil.toTitle(domainObject);
String message = localizeException(domainObject, exception);
exHandler.displayError(domainObject, exception, title, message);
} else {
exHandler.exceptionSwallowed(domainObject, exception);
}
swallowed = true;
}
if (exception instanceof RuntimeException) {
throw (RuntimeException) exception;
} else if (exception instanceof Error) {
throw (Error) exception;
} else if (ReflectionUtil.isUnswallowable(child)) {
throw new SevereGUIException(child);
}
return swallowed;
}
private String localizeException(Object domainObject, Throwable exception) {
Class<?> domainClass = getClassForResourceLookup();
String resourceMessage = I18NLabelModelFactory.createLabelModel(context, domainClass, exception.getMessage()).getObject();
if (StringUtils.isNotBlank(resourceMessage)) {
return resourceMessage;
} else {
return AbstractRootFrame.localizeException(domainObject, exception);
}
}
public DomainObjectReference getRef() {
if (ref == null) {
DomainElementI<DomainObjectReference> domainElement = context.getDomainRegistry().getElement(wicketId);
ref = (domainElement != null) ? domainElement.getAccessor().getRef() : null;
}
return ref;
}
protected String getHelp(Method relevantMethod) {
if (help == null) {
if (relevantMethod != null) {
Prompt prompt = new AnnotationHelper(relevantMethod).getAnnotation(Prompt.class);
if (prompt != null && !StringUtils.isEmpty(prompt.example()))
help = prompt.example().trim();
}
}
return help;
}
private Method nullableMethod(Method method) {
return (method == NULL_METHOD) ? null : method;
}
public Method getGetterMethod() {
initializeTransientMembersFromPrototype();
if (getterMethod == null) {
getterMethod = findPropertyAccessor(propertyName, ReflectionUtil.GETTER_PREFIX);
if (getterMethod == null) {
getterMethod = findPropertyAccessor(propertyName, ReflectionUtil.BOOLEAN_GETTER_PREFIX);
}
if (getterMethod == null)
getterMethod = NULL_METHOD;
}
return nullableMethod(getterMethod);
}
public Method getButtonMethod() {
initializeTransientMembersFromPrototype();
if (buttonMethod == null) {
buttonMethod = ReflectionUtil.findMethod(getRef().getDomainClass(), propertyName);
if (buttonMethod == null)
buttonMethod = NULL_METHOD;
}
return nullableMethod(buttonMethod);
}
public Method getChoicerMethod() {
initializeTransientMembersFromPrototype();
if (choicerMethod == null) {
choicerMethod = findPropertyAccessor(propertyName, ReflectionUtil.CHOICE_PREFIX);
if (choicerMethod == null)
choicerMethod = NULL_METHOD;
}
return nullableMethod(choicerMethod);
}
public Method getSetterMethod() {
initializeTransientMembersFromPrototype();
if (setterMethod == null) {
setterMethod = findPropertyAccessor(propertyName, ReflectionUtil.SETTER_PREFIX);
if (setterMethod == null)
setterMethod = NULL_METHOD;
}
return nullableMethod(setterMethod);
}
public Method getDisablerMethod() {
initializeTransientMembersFromPrototype();
if (disablerMethod == null) {
disablerMethod = findPropertyAccessor(propertyName, ReflectionUtil.DISABLER_PREFIX);
if (disablerMethod == null)
disablerMethod = NULL_METHOD;
}
return nullableMethod(disablerMethod);
}
public Method getHiderMethod() {
initializeTransientMembersFromPrototype();
if (hiderMethod == null) {
hiderMethod = findPropertyAccessor(propertyName, ReflectionUtil.HIDE_PREFIX);
if (hiderMethod == null)
hiderMethod = NULL_METHOD;
}
return nullableMethod(hiderMethod);
}
public Method getValidatorMethod() {
initializeTransientMembersFromPrototype();
if (validatorMethod == null) {
validatorMethod = findPropertyAccessor(propertyName, ReflectionUtil.VALIDATOR_PREFIX);
if (validatorMethod == null)
validatorMethod = NULL_METHOD;
}
return nullableMethod(validatorMethod);
}
public Method getRemoverButtonMethod() {
initializeTransientMembersFromPrototype();
if (removerButtonMethod == null) {
if (propertyName.startsWith(ReflectionUtil.REMOVER_PREFIX)) {
removerButtonMethod = getButtonMethod();
} else {
removerButtonMethod = findPropertyAccessor(propertyName, ReflectionUtil.REMOVER_PREFIX);
}
if (removerButtonMethod == null)
removerButtonMethod = NULL_METHOD;
}
return nullableMethod(removerButtonMethod);
}
public Object getButtonMethodTargetObject() {
return getMethodTargetObject("");
}
private Object getMethodTargetObject(String methodType) {
if (forcedMethodTargetObject != null) {
return forcedMethodTargetObject;
} else {
DomainObjectDecoration interception = getRef().getDecorationsAbsolute(wicketId);
Object targetObject = interception.getTarget(methodType, getRef());
if (targetObject == null && ReflectionUtil.GETTER_PREFIX.equals(methodType)) {
targetObject = interception.getTarget(ReflectionUtil.BOOLEAN_GETTER_PREFIX, getRef());
}
return targetObject;
}
}
public void setForcedMethodTargetObject(Object forcedMethodTargetObject) {
this.forcedMethodTargetObject = forcedMethodTargetObject;
}
/**
* This is tricky: The general rule is the same as in gengui. The component
* is statically disabled if there exists a getter but if the corresponsing
* setter is statically not available (directly or indirectly) . For the web
* we additionally have to take the case of a download button into account
* which is rendered for a read-only file attribute. In this case the
* corresponding download button must <b>not</b> be statically disabled.
*/
public boolean isStaticallyDisabled() {
initializeTransientMembersFromPrototype();
if (isStaticallyDisabled == null) {
isStaticallyDisabled = !isFileType(getGetterMethod()) && isStaticallyDisabled(getGetterMethod(), getSetterMethod(), getRef());
}
return isStaticallyDisabled;
}
public boolean isStaticallyDisabled(Method pGetterMethod, Method pSetterMethod, AbstractDomainReference ref) {
return pGetterMethod != null && //
(pSetterMethod == null || ReflectionUtil.isStaticallyDisabled(pSetterMethod, ref)) && //
!enableThoughUnmodifiable; //
}
public boolean isEnabled() {
return !isStaticallyDisabled() && !getRef().isDisabled() && (getDisablerMethod() == null || invokeDisablerMethod() == null)
&& !isInvalidReadonlyFile();
}
protected boolean isReadonlyFileProvider() {
return isFileType(getGetterMethod()) && //
(getSetterMethod() == null || ReflectionUtil.isStaticallyDisabled(getSetterMethod(), getRef()));
}
protected boolean isInvalidReadonlyFile() {
return isReadonlyFileProvider() && invalidFileValueMessage() != null;
}
protected String invalidFileValueMessage() {
File file = (File) invokeGetterMethod();
if (file != null && !file.isAbsolute())
file = new File(WebApplication.get().getServletContext().getRealPath(".") + "/" + file.getPath());
if (file == null)
return "file.error.null";
if (!file.exists())
return "file.error.invalid";
if (!file.canRead())
return "file.error.not.readable";
if (file.isDirectory())
return "file.error.directory";
return null;
}
protected String getTooltip(Method relevantMethod) {
String disabledMessage = null;
if (getDisablerMethod() != null) {
disabledMessage = (String) invokeDisablerMethod();
}
if (disabledMessage == null && isReadonlyFileProvider()) {
disabledMessage = invalidFileValueMessage();
}
if (disabledMessage != null) {
Class<?> domainClass = getClassForResourceLookup();
String resourceMessage = I18NLabelModelFactory.createLabelModel(context, domainClass, disabledMessage).getObject();
if (StringUtils.isNotBlank(resourceMessage)) {
return resourceMessage;
} else {
return disabledMessage;
}
} else {
return getHelp(relevantMethod);
}
}
public String getButtonTooltip() {
return getTooltip(getButtonMethod());
}
public String getFieldTooltip() {
return getTooltip(getGetterMethod());
}
public boolean isEager() {
initializeTransientMembersFromPrototype();
if (isEager == null) {
isEager = isAnnotationPresent(getSetterMethod(), Eager.class);
}
return isEager;
}
public boolean isProperty() {
Method method = findPropertyAccessor(getPropertyName(), ReflectionUtil.SETTER_PREFIX);
return method != null;
}
public boolean isEagerProperty(String propertyName) {
Method method = findPropertyAccessor(propertyName, ReflectionUtil.SETTER_PREFIX);
return isAnnotationPresent(method, Eager.class);
}
public boolean isAssisted() {
return isAnnotationPresent(getSetterMethod(), Assisted.class);
}
public boolean isForced() {
if(getButtonMethod() != null) {
return isAnnotationPresent(getButtonMethod(), Forced.class);
} else {
return isAnnotationPresent(getSetterMethod(), Forced.class);
}
}
private boolean isAnnotationPresent(Method method, Class<? extends Annotation> annotation) {
if (method != null) {
AnnotationHelper annotationHelper = new AnnotationHelper(method);
return annotationHelper.isAnnotationPresent(annotation);
} else {
return false;
}
}
/**
* IF there is not the getter is not annotated with @notnull, a null vlaue
* should be adde
*
* @param method
* the getter
* @param choices
* the possible values
* @return the possible value and null (perhaps)
*/
private Object[] addNullIfNullable(Method method, Object[] choices) {
if (isRequired(method)) {
return choices;
}
Object[] result = new Object[choices.length + 1];
for (int i = 0; i < choices.length; i++)
result[i + 1] = choices[i];
return result;
}
public boolean isRequired(Method method) {
return method.getAnnotation(NotNull.class) != null;
}
public Object[] getChoices() {
Class<?> propertyType = getGetterMethod().getReturnType();
if (propertyType == boolean.class) {
return new Object[] { true, false };
} else if (propertyType == Boolean.class) {
return new Object[] { Boolean.TRUE, Boolean.FALSE, null };
} else if (propertyType.isEnum()) {
if (getChoicerMethod() != null)
return invokeChoicer();
if (getGetterMethod().getReturnType().isPrimitive())
return propertyType.getEnumConstants();
else {
// IF there is not the getter is not annotated with @notnull, a
// null vlaue should be adde
return addNullIfNullable(getGetterMethod(), propertyType.getEnumConstants());
}
} else {
return invokeChoicer();
}
}
Object[] invokeChoicer() {
Object result = invokeChoicerMethod();
if (result != null && !result.getClass().isArray() && !Collection.class.isAssignableFrom(result.getClass())) {
Method choicerMethod = getChoicerMethod();
throw new IllegalArgumentException("Return type " + result.getClass().getName() + " is not valid for "
+ choicerMethod.getName() + " in class " + choicerMethod.getDeclaringClass().getName());
}
if (result == null)
return new Object[0];
if (Collection.class.isAssignableFrom(result.getClass())) {
return ReflectionUtil.boxCollection((Collection<?>) result);
} else {
return ReflectionUtil.boxArray(result);
}
}
public boolean isChoicesNullValid() {
for (Object c : getChoices()) {
if (c == null) {
return true;
}
}
return false;
}
public String getFormat() {
return getFormat(getGetterMethod());
}
public String getFormat(Method getterMethod) {
if (getterMethod != null) {
String format = GUIItemFactory.extractOutputFormat(getterMethod);
if (format == null && isDateType(getterMethod)) {
return DateSynchronizer.defaultOutFormat;
} else if (format == null && isNumberType(getterMethod)) {
return GeneratedNumberTextField.DEFAULT_DECIMAL_FORMAT_STR;
} else {
return format;
}
} else {
return null;
}
}
public void assertValidate() {
if (getValidatorMethod() == null && isAnnotationPresent(getGetterMethod(), Validate.class)) {
throw new SevereGUIException("Property " + propertyName + " requires a validation method");
}
}
/**
* Let the Ajax response update all components in all forms. Will be used by
* ajax buttons.
*
* @param ctx
* @param target
*/
public static void updateAllFormsFromPage(DMDWebGenPageContext ctx, final AjaxRequestTarget target) {
MarkupContainer root = findRoot(ctx.getPage());
root.visitChildren(new IVisitor<Component, Object>() {
@Override
public void component(Component object, IVisit<Object> visit) {
if (object instanceof Form || object instanceof ModalWindow || object instanceof TabbedPanel) {
Component component = object;
target.add(component);
}
IModel<?> defaultModel = object.getDefaultModel();
if (defaultModel instanceof TouchedListenerModelWrapper<?>) {
try {
((TouchedListenerModelWrapper<?>) defaultModel).preserveState(object);
} catch (Exception e) {
// Bei Fehler nix tun
}
}
}
});
Component feedback = ctx.getComponentRegistry().getComponent(FeedbackElement.DEFAULT_WICKET_ID);
if (feedback != null) {
target.add(feedback);
}
}
public void updateAllForms(final AjaxRequestTarget target) {
updateAllForms(getContext(), target);
}
public static void updateAllForms(DMDWebGenPageContext ctx, final AjaxRequestTarget target) {
MarkupContainer root = findRoot(ctx.getPage());
root.visitChildren(new IVisitor<Component, Object>() {
@Override
public void component(Component object, IVisit<Object> visit) {
IModel<?> defaultModel = object.getDefaultModel();
if (defaultModel instanceof TouchedListenerModelWrapper<?>) {
boolean isChanged = false;
try {
isChanged = ((TouchedListenerModelWrapper<?>) defaultModel).modelChangedBetweenRequestProcessing(object);
} catch (Exception e) {
// Bei Fehler nix tun
}
if (isChanged) {
((TouchedListenerModelWrapper<?>) defaultModel).preserveState(object);
target.add(object);
}
// TODO blaz02: Wicket6. Zuerst den Checkbox oder
// RadioChoice immer zum
// Target hinzuf�gen. Die Methode
// "modelChangedBetweenrEquestsProcessing"
// liefert immer false. -> F�r eine Checkbox oder ein
// RadioChoice muss es entweder einen eigenen
// TouchedListenerModelWrapper oder ein eigenes State, so
// das das eqausl anders programmiert wird. Der Weg ist ein
// schlechter Hack.
if (object instanceof CheckBox || object instanceof RadioChoice) {
target.add(object);
}
} else if (object instanceof GeneratedButton) {
GeneratedButton button = (GeneratedButton) object;
// vocke03: DMDVIER-155
if (button.hasButtonStateChanged()) {
target.add(button);
}
}
// Zwar ist das GenericDataTablePanel eine generische
// Komponente, aber der Inhalt dieser Tabelle nicht. So kann
// nicht festgestellt werden, ob sich der Inhalt
// der Tabelle ge�ndert hat. Deswegen muss jedesmal die
// komplette Tabelle in das Ajax-Target geschrieben werden.
// Bl�de L�sung! meis026
if (object instanceof GenericDataTablePanel) {
target.add(object);
}
}
});
Component feedback = ctx.getComponentRegistry().getComponent(FeedbackElement.DEFAULT_WICKET_ID);
if (feedback != null) {
target.add(feedback);
}
}
public static MarkupContainer findRoot(MarkupContainer page) {
MarkupContainer root = null;
if (page != null) {
root = findRoot(page.getParent());
}
return root != null ? root : page;
}
public boolean isBooleanType() {
return isBooleanType(getGetterMethod());
}
public static boolean isBooleanType(Method getter) {
Class<?> returnType = getter.getReturnType();
return returnType == boolean.class || returnType == Boolean.class;
}
public boolean isDateType() {
return isDateType(getGetterMethod());
}
public static boolean isDateType(Method getter) {
return Date.class.isAssignableFrom(getter.getReturnType());
}
public static boolean isFileType(Method getter) {
return getter != null && File.class.isAssignableFrom(getter.getReturnType());
}
public boolean isNumberType() {
return isNumberType(getGetterMethod());
}
public static boolean isNumberType(Method getter) {
Class<?> returnType = getter.getReturnType();
for (Class<?> clazz : new Class[] { Number.class, byte.class, short.class, int.class, long.class, float.class, double.class }) {
if (clazz.isAssignableFrom(returnType)) {
return true;
}
}
return false;
}
public boolean isRangedNumberType() {
return isRangedNumberType(getGetterMethod());
}
public boolean isInputStreamType() {
return isInputStreamType(getGetterMethod());
}
public static boolean isInputStreamType(Method getter) {
return getter != null && InputStream.class.isAssignableFrom(getter.getReturnType());
}
/**
* Can be used to check if range validation is possible on this number.
*/
public static boolean isRangedNumberType(Method getter) {
Class<?> returnType = getter.getReturnType();
for (Class<?> clazz : new Class[] { Byte.class, byte.class, Short.class, short.class, Integer.class, int.class, Long.class,
long.class, Float.class, float.class, double.class }) {
if (clazz.isAssignableFrom(returnType)) {
return true;
}
}
return false;
}
/**
* Performs a lazy initialization of all transient members which can be
* derived from a cached prototype instance. Running this initialization
* lazy has the advantage that it works for the case of new construction as
* well as for deserialization. Calling the initialization from within a
* readObject deserialization method turn out not to work well because the
* dependent members may not completely be initializwed at that time.
*/
protected void initializeTransientMembersFromPrototype() {
if (!transientFieldsInitializedFromPrototype) {
transientFieldsInitializedFromPrototype = true;
String syncName = synchronizerLookupName();
SynchronizerHelper syncPrototype = synchronizerPrototypes.get(syncName);
if (syncPrototype != null) {
getterMethod = syncPrototype.getterMethod;
setterMethod = syncPrototype.setterMethod;
choicerMethod = syncPrototype.choicerMethod;
disablerMethod = syncPrototype.disablerMethod;
hiderMethod = syncPrototype.hiderMethod;
validatorMethod = syncPrototype.validatorMethod;
buttonMethod = syncPrototype.buttonMethod;
removerButtonMethod = syncPrototype.removerButtonMethod;
isEager = syncPrototype.isEager;
isStaticallyDisabled = syncPrototype.isStaticallyDisabled;
help = syncPrototype.help;
}
}
}
/**
* This method has the only intention to be there and serve as an indicator
* for non-existent methods. See the initialization of the static member
* NULL_METHOD. Will be used by Reflection.
*/
@SuppressWarnings("unused")
private static void NULL_METHOD() {
}
public <T extends Annotation> T getAnnotationOnButtonMethode(Class<T> classAnnotation) {
return buttonMethod.getAnnotation(classAnnotation);
}
public static void synchronizeModelsForValidInput(Form<?> form) {
FormComponent.visitFormComponentsPostOrder(form, new ValidFormComponentSynchronizationVisitor(form));
synchronizeModelsForValidNestedInput(form);
}
protected static void synchronizeModelsForValidNestedInput(Form<?> form) {
Visits.visitChildren(form, new IVisitor<Form<?>, Void>() {
@Override
public void component(final Form<?> form, final IVisit<Void> visit) {
if (form.isEnabledInHierarchy() && form.isVisibleInHierarchy()) {
synchronizeModelsForValidInput(form);
} else {
visit.dontGoDeeper();
}
}
}, new ClassVisitFilter(Form.class));
}
public String getPrompt() {
if (prompt == null) {
Method getter = getGetterMethod();
if (getter != null) {
Prompt promptAnnotation = new AnnotationHelper(getter).getAnnotation(Prompt.class);
if (promptAnnotation != null && !StringUtils.isEmpty(promptAnnotation.value()))
prompt = promptAnnotation.value();
}
}
return prompt;
}
/**
* Copied from {@link gengui.domain.DomainObjectReference} because the
* GenGUI-implementierung uses a String-concat of accessType and
* propertyName when searching for the method rather than using the
* dedicated finder-method with prefix parameter supplied by
* ReflectionUtils.
*/
private Method findPropertyAccessor(String propertyName, String accessType) {
DomainObjectDecoration decoration = getRef().getDecorationsRelative(propertyName);
final Class<?> targetClass = decoration.getTargetClass(accessType, getRef());
Method targetMethod = (ReflectionUtil.isGetterPrefix(accessType)) ? ReflectionUtil.findGetterMethod(targetClass,
propertyName) : ReflectionUtil.findMethod(targetClass, accessType, propertyName);
if (targetMethod == null && targetClass != getRef().getDomainClass()) {
DefaultSevereExceptionHandler.handler().process(
"Class " + targetClass.getName() + " defines a " + accessType
+ " decorator with a different name than expected '" + accessType + propertyName + "'");
}
return targetMethod;
}
protected static class ValidFormComponentSynchronizationVisitor implements IVisitor<FormComponent<?>, Void> {
private final Form<?> formToVisit;
public ValidFormComponentSynchronizationVisitor(Form<?> formToVisit) {
this.formToVisit = formToVisit;
}
@Override
public void component(final FormComponent<?> formComponent, final IVisit<Void> visit) {
final Form<?> componentsParent = Form.findForm(formComponent);
if (componentsParent == formToVisit) {
if (componentsParent.isEnabledInHierarchy() && formComponent.isVisibleInHierarchy() && formComponent.isEnabledInHierarchy()) {
formComponent.validate();
if (formComponent.isValid()) {
formComponent.updateModel();
}
}
}
}
}
}